package com.jack.netty.tcp.client;

import com.jack.netty.coder.tlv.TLVDecoder;
import com.jack.netty.coder.tlv.TLVEncoder;
import com.jack.netty.log.DefaultLogger;
import com.jack.netty.log.LoggerManager;
import com.jack.netty.util.NetUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * Created by jack on 15/9/26.
 */
public class TLVClient {

    private String ip = "127.0.0.1";
    private int port = 8899;
    private String clientName = "客户端";

    private int idleTimeRead = 5;
    private int idleTimeWrite = 5;

    private EventLoopGroup workergroup;
    private Bootstrap bootstrap;

    private TLVClientHandler clientHandler = null;

    private boolean manualCloseFlag = false;
    private boolean restartFlag = true;

    private TLVClientListener tlvClientListener = null;

    private int restartTime = 3;

    /**
     * 是否检查网络可用超时次数
     */
    private boolean checkNetTimeOut = false;

    /**
     * 检查网络可用超时次数
     */
    private int netTimeOut = 60*60;

    public TLVClient(){

    }

    /**
     * 启动服务,需要启动单独线程使用
     */
    public void start(){

        if(manualCloseFlag){
            LoggerManager.getInstance().getiLogger().info(getClass(),"手动关闭"+clientName+"服务");
            return;
        }

        //判断网络是否可用
        int timeoutCount = 0;
        while(NetUtil.getAllIps(2).size() < 1){
            try{
                timeoutCount++;
                LoggerManager.getInstance().getiLogger().error(getClass(),"请检查网络是否可用,检查失败次数:"+timeoutCount);

                if(!restartFlag){
                    LoggerManager.getInstance().getiLogger().warn(getClass(),"检测网络有问题且连接被设置不自动重连,关闭连接");
                    return;
                }

                if(checkNetTimeOut && timeoutCount > netTimeOut){
                    LoggerManager.getInstance().getiLogger().warn(getClass(),"检测网络有问题且失败次数已超过"+netTimeOut+"次,关闭连接");
                    return;
                }

                Thread.sleep(1000);
            }catch (Exception e){
                LoggerManager.getInstance().getiLogger().error(getClass(),"网络检测暂停错误:"+e.getMessage());
            }
        }

        try{
            workergroup = new NioEventLoopGroup();
            bootstrap = new Bootstrap();
            bootstrap.channel(NioSocketChannel.class);
            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
            bootstrap.option(ChannelOption.TCP_NODELAY, true);
            bootstrap.group(workergroup);
            bootstrap.handler(new LoggingHandler(LogLevel.INFO));
            //bootstrap.remoteAddress(ip, port);

            clientHandler = new TLVClientHandler(tlvClientListener);
            bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    socketChannel.pipeline().addLast(new IdleStateHandler(idleTimeRead, idleTimeWrite, 0));
                    socketChannel.pipeline().addLast(new TLVEncoder());
                    socketChannel.pipeline().addLast(new TLVDecoder());
                    socketChannel.pipeline().addLast(clientHandler);
                }
            });
            ChannelFuture channelFuture = bootstrap.connect(ip,port).sync();
            channelFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture f) throws Exception {
                    if (f.isSuccess()) {
                        LoggerManager.getInstance().getiLogger().info(getClass(),clientName + "启动成功!");
                    } else {
                        if (restartFlag) {
                            LoggerManager.getInstance().getiLogger().info(getClass(),clientName + "启动失败,重启中...");
                            f.channel().eventLoop().schedule(new Runnable() {
                                @Override
                                public void run() {
                                    start();
                                }
                            }, restartTime, TimeUnit.SECONDS);
                        } else {
                            LoggerManager.getInstance().getiLogger().error(getClass(),clientName + "启动失败");
                        }

                    }
                }
            });
            channelFuture.channel().closeFuture().sync();

        }catch (Exception e){
            LoggerManager.getInstance().getiLogger().error(getClass(),clientName + "启动异常:" + e.getMessage());
            restart();
        }finally {
            LoggerManager.getInstance().getiLogger().info(getClass(),clientName + "关闭前检测");
            workergroup.shutdownGracefully();
            workergroup = null;
            if(restartFlag){
                restart();
            }
        }
    }

    /**
     * 重启服务器,内部调用
     */
    private void restart(){
        try {
            Thread.sleep(restartTime*1000);
        } catch (InterruptedException e) {
            LoggerManager.getInstance().getiLogger().error(getClass(),"重启暂停错误:"+e.getMessage());
        }
        start();
    }

    /**
     * 关闭服务,调用此方法如果不做其他的动作的话,服务会自动停止
     */
    public void stop(){
        manualCloseFlag = true;
        if(workergroup != null){
            workergroup.shutdownGracefully();
        }
    }

    /**
     * 发送消息到服务端
     * @param message 消息内容
     * @return
     */
    public boolean sendMessage(String message){
        boolean sendFlag = false;

        LoggerManager.getInstance().getiLogger().info(getClass(),"要发送的消息");//+message
        if(clientHandler != null){
            if(clientHandler.getChannelHandlerContext() != null){
                clientHandler.getChannelHandlerContext().writeAndFlush(message);
                sendFlag = true;
                LoggerManager.getInstance().getiLogger().info(getClass(),"发送消息到:"+clientHandler.getChannelHandlerContext().channel().id().asLongText());
            }
        }
        return sendFlag;
    }

    /**
     * 获取服务器唯一ID
     * @return
     */
    public String getServerId(){
        if(clientHandler != null){
            return clientHandler.getServerId();
        }

        return System.currentTimeMillis()+"";
    }

    /**
     * 获取服务器ChannelHandlerContext
     * @return ChannelHandlerContext
     */
    public ChannelHandlerContext getServerChannelHandlerContext(){
        if(clientHandler != null){
            return clientHandler.getChannelHandlerContext();
        }

        return  null;
    }

    public String getIp() {
        return ip;
    }

    /**
     * 设置服务端IP
     * @param ip
     */
    public void setIp(String ip) {
        this.ip = ip;
    }

    public int getPort() {
        return port;
    }

    /**
     * 设置服务端端口
     * @param port
     */
    public void setPort(int port) {
        this.port = port;
    }

    public String getClientName() {
        return clientName;
    }

    /**
     * 设置服务端名称,用户日志区分
     * @param clientName
     */
    public void setClientName(String clientName) {
        this.clientName = clientName;
    }

    public int getIdleTimeRead() {
        return idleTimeRead;
    }

    /**
     * 设置读超时时间
     * @param idleTimeRead
     */
    public void setIdleTimeRead(int idleTimeRead) {
        this.idleTimeRead = idleTimeRead;
    }

    public int getIdleTimeWrite() {
        return idleTimeWrite;
    }

    /**
     * 设置写超时时间
     * @param idleTimeWrite
     */
    public void setIdleTimeWrite(int idleTimeWrite) {
        this.idleTimeWrite = idleTimeWrite;
    }

    public boolean isManualCloseFlag() {
        return manualCloseFlag;
    }

    /**
     * 设置手动关闭服务标示
     * 一旦设置成true,在连接断开后不会再自动重启
     * @param manualCloseFlag
     */
    public void setManualCloseFlag(boolean manualCloseFlag) {
        this.manualCloseFlag = manualCloseFlag;
    }

    public boolean isRestartFlag() {
        return restartFlag;
    }

    /**
     * 设置重启标示
     * @param restartFlag
     */
    public void setRestartFlag(boolean restartFlag) {
        this.restartFlag = restartFlag;
    }

    public TLVClientListener getTlvClientListener() {
        return tlvClientListener;
    }

    /**
     * 设置消息处理器
     * @param tlvClientListener
     */
    public void setTlvClientListener(TLVClientListener tlvClientListener) {
        this.tlvClientListener = tlvClientListener;
    }

    /**
     * 判断是否已连接着服务器
     * 客户端也可自己去实现
     * @return
     */
    public boolean isConnected() {
        if(clientHandler != null){
            return clientHandler.isConnectedFlag();
        }

        return false;
    }

    public int getRestartTime() {
        return restartTime;
    }

    /**
     * 设置重启间隔时间,秒为单位
     * @param restartTime
     */
    public void setRestartTime(int restartTime) {
        this.restartTime = restartTime;
    }

    public int getNetTimeOut() {
        return netTimeOut;
    }

    public void setNetTimeOut(int netTimeOut) {
        this.netTimeOut = netTimeOut;
    }

    public boolean isCheckNetTimeOut() {
        return checkNetTimeOut;
    }

    public void setCheckNetTimeOut(boolean checkNetTimeOut) {
        this.checkNetTimeOut = checkNetTimeOut;
    }

    public static void main(String[] args){
        LoggerManager.getInstance().setiLogger(new DefaultLogger());
        int clientCount = 1;
        for(int i = 0 ; i < clientCount ; i++){

            final TLVClient client = new TLVClient();
            client.setIp("120.26.199.77");
            //client.setIp("127.0.0.1");
            client.setPort(53654);

            client.setTlvClientListener(new TLVClientListener() {
                @Override
                public void onConnect(String serverId) {

                    client.sendMessage("{\n" +
                            "  \"boxModel\": {\n" +
                            "    \"boxCpuId\": \"3400503443146021018e\", \n" +
                            "    \"boxInsideIp\": \"192.168.31.172\", \n" +
                            "    \"boxName\": \"jack主机\", \n" +
                            "    \"id\": 1, \n" +
                            "    \"lastUpdateTime\": 0, \n" +
                            "    \"status\": \"online\", \n" +
                            "    \"userName\": \"ok\"\n" +
                            "  }, \n" +
                            "  \"currentBoxCpuId\": \"3400503443146021018e\", \n" +
                            "  \"type\": 331, \n" +
                            "  \"userModel\": {\n" +
                            "    \"id\": 105, \n" +
                            "    \"ip\": \"192.168.0.130\", \n" +
                            "    \"lastUpdateTime\": 0, \n" +
                            "    \"nickName\": \"JACK\", \n" +
                            "    \"password\": \"hOCV0KcQ9Ihj1DIC7umDHg==\", \n" +
                            "    \"phone\": \"15155137761\", \n" +
                            "    \"userName\": \"ok\"\n" +
                            "  }\n" +
                            "}");
                }

                @Override
                public void onDisconnect(String serverId) {

                }

                @Override
                public void onMessage(String serverId,String message) {

                }
            });

            new Thread(new Runnable() {
                @Override
                public void run() {
                    client.start();
                }
            }).start();
        }
    }


}
